﻿#include "VM.h"
#include "vm_instructions.h"


namespace yzrilyzr_vmp{
	VM::~VM(){
		/*for(auto & a : modules){
			delete a.second;
		}*/
	}
	std::shared_ptr<VMModule> VM::getModule(const std::string & moduleName){
		return modules[moduleName];
	}

	void VM::loadModule(const std::string & moduleName, const VMBinaryCode & code){
		std::shared_ptr<VMModule> modulePtr=std::make_shared<VMModule>();
		VMModule & module=*modulePtr;
		module.name=moduleName;
		module.code=code.code;
		module.codeLength=code.length;
		//load
		execute(module, module.global, 0);
		modules[moduleName]=modulePtr;
	}
	VMValue VM::callFunction(const std::string & moduleName, const std::string & funcName, VMValue * args, u_index argLen){
		VMModule & module=*modules[moduleName];
		return callFunction(module, funcName, args, argLen);
	}
	VMValue VM::callFunction(VMModule & module, const std::string & funcName, VMValue * args, u_index argLen){
		VMStackFrame function;
		function.prev=&module.global;
		return execute(module, function, module.exportFunctionEntryPoint[funcName]);
	}
	VMValue VM::getVariable(const std::string & moduleName, const std::string & varName){
		VMModule & module=*modules[moduleName];
		return getVariable(module, varName);
	}
	VMValue VM::getVariable(VMModule & module, const std::string & varName){
		VMStackFrame & global=module.global;
		u_index pos=global.namedLocalVariables[varName];
		VMValue val=global.localVariables[pos];
		return val;
	}

	VMValue VM::execute(VMModule & module, VMStackFrame & stackFrame, u_index entryPoint){
		BufferReader reader(module.code, module.codeLength);
		reader.seek(entryPoint);
		while(reader.has()){
			uint8_t opcode=reader.readUByte();
			switch(opcode){
				case VMI::IMPORT_SYM:
				{
					std::string name=reader.readString();
					VMValue & modName=stackFrame.stack.top();
					VMValue result=getVariable(modName.str, name);
					stackFrame.stack.push(result);
					break;
				}
				case VMI::EXPORT_SYM:
				{
					std::string name=reader.readString();
					VMValue & result=getVariable(module, name);
					stackFrame.stack.push(result);
					break;
				}
				case VMI::ADD_I32:
				{
					VMValue & v1=stackFrame.stack.top(); stackFrame.stack.pop();
					VMValue & v2=stackFrame.stack.top(); stackFrame.stack.pop();
					VMValue result;
					result.type=VMValueType::I32;
					result.i32=v1.i32 + v2.i32;
					stackFrame.stack.push(result);
					break;
				}
				case VMI::ADD_I64:
				{
					VMValue & v1=stackFrame.stack.top(); stackFrame.stack.pop();
					VMValue & v2=stackFrame.stack.top(); stackFrame.stack.pop();
					VMValue result;
					result.type=VMValueType::I64;
					result.i64=v1.i64 + v2.i64;
					stackFrame.stack.push(result);
					break;
				}
				case VMI::ADD_F32:
				{
					VMValue & v1=stackFrame.stack.top(); stackFrame.stack.pop();
					VMValue & v2=stackFrame.stack.top(); stackFrame.stack.pop();
					VMValue result;
					result.type=VMValueType::F32;
					result.f32=v1.f32 + v2.f32;
					stackFrame.stack.push(result);
					break;
				}
				case VMI::ADD_F64:
				{
					VMValue & v1=stackFrame.stack.top(); stackFrame.stack.pop();
					VMValue & v2=stackFrame.stack.top(); stackFrame.stack.pop();
					VMValue result;
					result.type=VMValueType::F64;
					result.f64=v1.f64 + v2.f64;
					stackFrame.stack.push(result);
					break;
				}
				case VMI::LOAD_IMM_32:
				{
					VMValue v1;
					v1.i32=reader.readUInt();
					stackFrame.stack.push(v1);
					break;
				}
				case VMI::LOAD_IMM_64:
				{
					VMValue v1;
					v1.i64=reader.readULong();
					stackFrame.stack.push(v1);
					break;
				}

				case VMI::RETURN:
				{
					VMValue v1=stackFrame.stack.top(); stackFrame.stack.pop();
					return v1;
				}
				case VMI::JUMP_ADDR:
				{
					u_index pt=reader.readULong();
					if(((pt >> 63) & 1) == 1){
						u_index p2=reader.getPointer();
						reader.seek(p2 + pt);
					} else{
						reader.seek(pt);
					}
					break;
				}
				case VMI::RETURN_VOID:
				{
					VMValue nullValue;
					nullValue.type=VMValueType::VOID;
					return nullValue;
				}
				case VMI::LOAD_LOCAL:
				{
					VMValue &v1=stackFrame.localVariables[0];
					stackFrame.stack.push(v1);
					break;
				}
				default:
					continue;
			}
		}
		VMValue nullValue;
		nullValue.type=VMValueType::VOID;
		return nullValue;
	}

}
